package fstvis;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import javax.swing.JPanel;

public class ColourBar extends JPanel {

	private static final long serialVersionUID = 1L;
	protected double minVal, maxVal, span;
	protected int res;
	protected ArrayList<Color> colours;
	int divs;
	
	public ColourBar(double min, double max, int divs) {
		init(min, max, divs, 10);
	}

	public ColourBar(double min, double max, int divs, int res) {
		init(min, max, divs, res);
	}
	
	private void init(double min, double max, int divs, int res) {
		this.minVal = min;
		this.maxVal = max;
		this.divs = divs;
		setRes(res);
		
		// get our span		
		span = maxVal - minVal;
		
		this.setOpaque(true);
		this.setMinimumSize(new Dimension(200, 400));
		
		colours = new ArrayList<Color>();
		switch (divs) {
			case 2:
				colours.add(Color.BLACK);
				colours.add(Color.WHITE);
				break;
			case 3:
				colours.add(Color.BLUE);
				colours.add(Color.GREEN);
				colours.add(Color.RED);
				break;
			case 4:
				colours.add(Color.BLUE);
				colours.add(Color.GREEN);
				colours.add(Color.YELLOW);
				colours.add(Color.RED);
				break;
			default:
				colours.add(Color.BLUE);
				colours.add(new Color(160, 32, 240)); // Purple
				colours.add(Color.GREEN);
				colours.add(Color.YELLOW);
				colours.add(Color.RED);
				break;
		}
		
//		// Reverse the colours..
//		Color[] oc = colours.clone();
//		for (int x=0; x<colours.length; x++) {
//			System.out.println(String.format("colours[%d] = oc[%d]",(colours.length-1)-x, x)); 
//			colours[(colours.length-1)-x] = oc[x];
//		}
		//System.out.println("Created colour bar, [" + min + ", " + max + "], " + divs + " divisions, res = " + res);
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		paintColourBar(g);
	}

	protected void paintColourBar(Graphics g) {
		int h=this.getHeight();
		int lw = 100; // legend width
		int w=this.getWidth() - lw;
		
		
		Color c;
		int x = 0;
		//System.out.println("Width: " + w + ", height: " + h);

		// Clear..
		g.setColor(Color.WHITE);
		g.fillRect(1, 1, w+lw, h);
		
		double val;
		int yp;
		for (int y = 0; y<h; y+=res) {
			yp = h - y;
			// Normalise y to the height, then scale to the span of the colour bar 
			val = (((double)y)/(double)h)*span + minVal;
			c = getColour(val);
			g.setColor(c);
			//System.out.println(String.format(" fillRect(%d, %d, %d, %d) for val=%5.3f", 1, y, w, res, val) + " with colour " + c);
			g.fillRect(1, yp, w, (int)res);
			if (x % divs == 0) {
				g.setColor(Color.black);
				g.drawString(String.format("%6.3f", val), w+10, yp);
			}
			x++;
		}
		//System.exit(1);
	}
	
	public Color getColour(double val) {
		Color c;

		//System.out.print(String.format("[%3.1f, %3.1f], span = %2.1f, orig val = %3.1f", minVal, maxVal, span, val));

		double orig_val = val;
		
		// Shift the value
		val -= minVal;
		
		// Normalise the value to the span
		val = val/span;
		// Value should now be between [0, 1]

//		if (Math.abs(val - 1.007) < 1e-4) {
//			System.err.println(String.format("orig val = %6.4f", orig_val));
//			System.err.println(String.format("Span     = %6.4f, [%6.4f, %6.4f]", span, minVal, maxVal));
//			System.err.println(String.format("New val = %6.4f", val));
//			System.exit(1);
//		}

		// This was put here to work out the bugs before this hides them.
		// If a min/max was manually set, val could shoot out of the [0,1] range, so lets put it back
		// in here.
		val = Math.min(val, 1); val = Math.max(val, 0);
		
		// Find the colours we're between
		double bs = 1.0/((double)divs - 1.0); // bin size
		int lower = (int)Math.floor(val/bs);
		lower = Math.max(lower, 0); // make sure it's never negative somehow.
		lower = Math.min(lower, colours.size() - 1); // Make sure it's never out of the array 
		int up   = lower + 1;
		up = Math.min(up, colours.size() - 1);

		if (lower == up) lower--;
		
		//System.out.println(String.format("Choosing [%d, %d], val = %3.3f, binsize = %3.2f", lower, up, val, bs));
		
		Color cl = colours.get(lower);
		Color cu = colours.get(up);

		//System.out.println("Interpolating between " + cl + " and " + cu);

		double threshold = 1e-1;
		
		if (Math.abs(orig_val) < threshold) {
			// Show actual zeros as gray
			c = Color.gray;
		} else {
			// Interpolate between these two colours;
			int r = interpolate(val, bs*(double)lower, cl.getRed(), bs*(double)up, cu.getRed());
			int g = interpolate(val, bs*(double)lower, cl.getGreen(), bs*(double)up, cu.getGreen());
			int b = interpolate(val, bs*(double)lower, cl.getBlue(),  bs*(double)up, cu.getBlue());
	
			//System.out.println(String.format("[%d, %d] Trying to set colour: %3.1f ==> (%d, %d, %d)", lower, up, val, r, g, b));
			try {
				c = new Color(r, g, b);
			} catch (java.lang.IllegalArgumentException ex) {
				System.out.println(String.format("[GETCOL] [%d, %d] Trying to set colour: %3.1f ==> (%d, %d, %d)", lower, up, val, r, g, b));
				System.out.println(String.format("[GETCOL] x = %6.4f, (%3.1f, %d), (%3.1f, %d) ==> %d", val, bs*(double)lower, cl.getGreen(), bs*(double)up, cu.getGreen(), interpolate(val, bs*(double)lower, cl.getGreen(), bs*(double)up, cu.getGreen())));
				System.out.println(String.format("[GETCOL] x = %6.4f, (%3.1f, %d), (%3.1f, %d) ==> %d", val, bs*(double)lower, cl.getBlue(), bs*(double)up, cu.getBlue(), interpolate(val, bs*(double)lower, cl.getBlue(),  bs*(double)up, cu.getBlue())));
				System.exit(1);
				c = Color.black;
			}
		}
			
		return c;
	}


	private static int interpolate(double x, double x0, double y0, double x1, double y1) {
		int res1 = (int)(y0 + ((x-x0)*(y1 - y0)/(x1 - x0)));
//		double res2 = (y0 + ((x-x0)*(y1 - y0)/(x1 - x0)));
//		if (res1 < 0 || res2 < 0) {
//			System.out.println(String.format("[INTERP] (%5.3f + (%5.3f-%5.3f)*(%5.3f - %5.3f)/(%5.3f - %5.3f)) = %3.1f", y0, x, x0, y1, y0, x1, x0, (y0 + ((x-x0)*(y1 - y0)/(x1 - x0)))));
//			System.out.println(String.format("[INTERP] (x-x0) = %5.3f, (y1 - y0) = %5.3f, (x1 - x0) = %5.3f", x-x0, y1 - y0, x1 - x0));
//			System.out.println(String.format("[INTERP] %3.2f + %3.2f", y0, ((x-x0)*(y1 - y0)/(x1 - x0))));
//			System.out.println(String.format("[INTERP] x = %3.1f, (%3.1f, %3.1f), (%3.1f, %3.1f) ==> %d, %5.3f", x, x0, y0, x1, y1, res1, res2));
//			System.out.println(String.format("[INTERP] %3.1f =?= %3.1f", (y0 + (((x - x0) * (y1 - y0)) / (x1 - x0))), (255.0 + (((1.0 - 0.8) * (0.0 - 255.0)) / (1.0 - 0.8)))));
//		}
		return res1;
	}
	
	public double getMinValue() {
		return this.minVal;
	}

	public double getMaxValue() {
		return this.maxVal;
	}
	
	public void setRes(int res) {
		this.res = Math.max(res, 1);
	}
	
	public static void main(String[] args) {
		// Test making colours..

		// First, test our interpolator
		double a_x1 = 0.8, a_y1 = 255, a_x2 = 1, a_y2 = 0, a_x=1;
		System.out.println(String.format("Range: [%1.0f, %1.0f], find %2.1f ==> %d", a_x1, a_x2, a_x, interpolate(a_x, a_x1, a_y1, a_x2, a_y2)));

		Color c1 = Color.GREEN;
		Color c2 = Color.RED;
		double bs=.8;
		
		double b_x=1;
		int b_interp = interpolate(b_x, bs*(double)1, c1.getGreen(), bs/.8*(double)1, c2.getGreen());
		System.out.println(String.format("Range: [%1.0f, %1.0f], find %2.1f ==> %d", bs*(double)1, bs*(double)0, b_x, b_interp));
		
//		ColourBar cb = new ColourBar(-5, 5, 4, 20);
//
//		JFrame f = new JFrame();
//	    f.setMinimumSize(new Dimension(200, 300));
//	    cb.setPreferredSize(f.getSize());
//	    f.add(cb);
//		f.setVisible(true);
//
//    	WindowListener wl = new java.awt.event.WindowAdapter() {
//    		public void windowClosing(WindowEvent winEvt) {
//	        	System.out.println("\nExiting...");
//				System.exit(0);
//    	    }
//    	};
//		
//    	f.addWindowListener(wl);
		System.out.println("Exiting");
	}
}
